package org.jason.user.service.impl;


import cn.dev33.satoken.stp.SaTokenInfo;
import cn.dev33.satoken.stp.StpUtil;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.mysql.cj.log.Log;
import org.jason.common.constants.LoginConstants;
import org.jason.common.exception.ServiceException;
import org.jason.model.common.dtos.ResponseResult;
import org.jason.model.common.enums.HttpCodeEnum;
import org.jason.model.common.enums.RoleEnum;
import org.jason.model.user.dtos.*;
import org.jason.model.user.pojos.CuUser;
import org.jason.model.user.pojos.CuUserRole;
import org.jason.model.user.pojos.UserRolePermission;
import org.jason.user.mapper.CuUserMapper;
import org.jason.user.service.ICuUserRoleService;
import org.jason.user.service.ICuUserService;
import org.jason.user.service.IUserRolePermService;
import org.jason.utils.MD5Util;
import org.jason.utils.SaltUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.regex.Pattern;

/**
 * <p>
 * 用户信息表 服务实现类
 * </p>
 *
 * @author jason
 * @since 2023-07-19
 */
@Service
@Transactional
public class CuUserServiceImpl extends ServiceImpl<CuUserMapper, CuUser> implements ICuUserService {

    //密码随机盐值位数
    private static final short SALT_NUMBER = 8;

    @Autowired
    private ICuUserRoleService userRoleService;

    @Autowired
    private IUserRolePermService userRolePermService;

    /**
     * 用户登录
     *
     * @param loginDto
     * @return
     */
    @Override
    public ResponseResult login(LoginDto loginDto) {
        //先判断是否登录
        boolean isLogin = StpUtil.isLogin();
        if (isLogin) {
            return ResponseResult.get(HttpCodeEnum.REPEAT_LOGIN);
        }
        //验证用户登录
        CuUser cuUser = this.loginCheck(loginDto);
        if (cuUser != null) {
            //登录
            StpUtil.login(cuUser.getId());
            //生成用户权限表保存到redis
            userRolePermService.uploadUserRolePermToRedis(cuUser.getId());
            //将用户信息和token返回给用户
            SaTokenInfo tokenInfo = StpUtil.getTokenInfo();
            Map<String, Object> map = new HashMap<>();
            map.put("user", cuUser);
            map.put("token", tokenInfo);
            return new ResponseResult<>(HttpCodeEnum.SUCCESS, "登录成功", map);
        }
        return ResponseResult.get(HttpCodeEnum.LOGIN_INFO_ERROR);
    }

    /**
     * 用户注册
     *
     * @param registerDto 注册表单
     * @return
     */
    @Override
    public ResponseResult<?> register(RegisterDto registerDto) {
        //查询用户是否存在
        String username = registerDto.getUsername();
        boolean exists = this.getBaseMapper().exists(Wrappers.<CuUser>lambdaQuery().eq(CuUser::getUsername, username));
        if (exists) {
            return ResponseResult.get(HttpCodeEnum.DATA_EXIST, "用户已注册！");
        }
        boolean exists1 = this.getBaseMapper().exists(Wrappers.<CuUser>lambdaQuery().eq(CuUser::getPhone, registerDto.getPhone()));
        if (exists1) {
            return ResponseResult.get(HttpCodeEnum.DATA_EXIST, "该手机号已绑定已存在账号！");
        }
        boolean exists2 = this.getBaseMapper().exists(Wrappers.<CuUser>lambdaQuery().eq(CuUser::getUsername, registerDto.getEmail()));
        if (exists2) {
            return ResponseResult.get(HttpCodeEnum.DATA_EXIST, "该邮箱已绑定已存在账号！");
        }
        //密码加盐后进行md5混淆
        String password = registerDto.getPassword();
        String salt = SaltUtil.getSalt(SALT_NUMBER);
        String passSalt = MD5Util.encodeWithSalt(password, salt);
        //装箱
        CuUser userReg = new CuUser()
                .setUsername(username)
                .setPassword(passSalt)
                .setSalt(salt);
        userReg.setPhone(registerDto.getPhone());
        userReg.setNickname(registerDto.getNickname());
        userReg.setEmail(registerDto.getEmail());
        userReg.setIntroduce(registerDto.getIntroduce());
        //注册
        boolean isSuccess = this.save(userReg);
        if (isSuccess) {
            //绑定新用户到普通用户
            userRoleService.bindUserRole(userReg, RoleEnum.USER);
            //注册成功自动登录
            StpUtil.login(userReg.getId());
            //将用户信息和token返回给用户
            SaTokenInfo tokenInfo = StpUtil.getTokenInfo();
            Map<String, Object> map = new HashMap<>();
            map.put("user", userReg);
            map.put("token", tokenInfo);
            return new ResponseResult<>(HttpCodeEnum.SUCCESS, "注册成功", map);
        }
        return ResponseResult.get(HttpCodeEnum.SERVER_ERROR, "注册失败");
    }

    /**
     * 获取个人信息
     *
     * @return 个人信息
     */
    @Override
    public ResponseResult<CuUser> getPersonalInfo() {
        int userId = StpUtil.getLoginIdAsInt();
        CuUser cuUser = this.getById(userId);
        return new ResponseResult<>(HttpCodeEnum.SUCCESS, "获取成功！", cuUser);
    }

    /**
     * 修改非敏感个人信息
     *
     * @param dto 个人信息
     * @return 修改结果
     */
    @Override
    public ResponseResult editPersonalInfo(EditPersonalInfoDto dto) {
        CuUser cuUser = new CuUser();
        int userId = StpUtil.getLoginIdAsInt();
        cuUser.setId(userId);
        String nickname = dto.getNickname();
        if (StringUtils.isNotBlank(nickname)) {
            cuUser.setNickname(nickname);
        }
        Integer sex = dto.getSex();
        if (StringUtils.isNotBlank(nickname)) {
            cuUser.setSex(sex);
        }
        String introduce = dto.getIntroduce();
        if (StringUtils.isNotBlank(introduce)) {
            cuUser.setIntroduce(introduce);
        }
        this.updateById(cuUser);
        return ResponseResult.get(HttpCodeEnum.SUCCESS, "修改成功！");
    }

    /**
     * 修改密码
     *
     * @param dto 旧密码 新密码
     * @return 修改结果
     */
    @Override
    public ResponseResult changePassword(ChangePasswordDto dto) {
        String oldPassword = dto.getOldPassword();
        String newPassword = dto.getNewPassword();
        if (oldPassword.equals(newPassword)) {
            return ResponseResult.get(HttpCodeEnum.PARAM_INVALID, "新密码不能和旧密码相同");
        }
        //验证密码是否正确
        int userId = StpUtil.getLoginIdAsInt();
        CuUser cuUser = this.getById(userId);
        //验证密码
        String cuUserPassSalt = cuUser.getPassword();
        String salt = cuUser.getSalt();
        //对用户输入的密码加盐比较
        String inputOldPassSalt = MD5Util.encodeWithSalt(oldPassword, salt);
        if (cuUserPassSalt.equals(inputOldPassSalt)) {
            //验证成功
            //对新密码进行加盐存储
            String inputNewPassSalt = MD5Util.encodeWithSalt(newPassword, salt);
            this.update(Wrappers.<CuUser>lambdaUpdate()
                    .eq(CuUser::getId, userId)
                    .set(CuUser::getPassword, inputNewPassSalt));
            return ResponseResult.get(HttpCodeEnum.SUCCESS);
        }
        return ResponseResult.get(HttpCodeEnum.PARAM_INVALID, "旧密码错误");
    }

    /**
     * 获取用户信息
     *
     * @param userId
     * @return
     */
    @Override
    public ResponseResult<CuUser> getUserInfo(Integer userId) {
        CuUser user = this.getById(userId);
        if (user != null) {
            return new ResponseResult<>(HttpCodeEnum.SUCCESS, user);
        }
        return new ResponseResult<>(HttpCodeEnum.DATA_NOT_EXIST);
    }

    /**
     * 修改用户头像
     *
     * @param dto
     */
    @Override
    public void changeAvatar(ChangeAvatarDto dto) {
        Integer userId = dto.getUserId();
        this.update(Wrappers.<CuUser>lambdaUpdate()
                .eq(CuUser::getId, userId)
                .set(CuUser::getImageUrl, dto.getAvatarUrl()));
    }

    /**
     * 后台登录
     *
     * @return
     */
    @Override
    public ResponseResult backstageLogin(LoginDto dto) {
        CuUser user = loginCheck(dto);
        if (user == null) {
            return ResponseResult.get(HttpCodeEnum.LOGIN_INFO_ERROR);
        }
        Integer userId = user.getId();
        UserRolePermission permission = userRolePermService.getUserRolePermission(userId).getData();
        List<CuUserRole> roles = permission.getRoles();
        //如果该用户只有一个角色并且这个角色是user
        if (roles.size() == 1 && roles.get(0).getRoleId() == 1) {
            return ResponseResult.get(HttpCodeEnum.NO_OPERATOR_AUTH);
        }
        StpUtil.login(userId);
        String token = StpUtil.getTokenValueByLoginId(userId);
        Map<String, Object> map = new HashMap<>();
        map.put("token", token);
        map.put("user", user);
        map.put("role", roles);
        return new ResponseResult<>(HttpCodeEnum.SUCCESS, "后台登录成功！", map);
    }

    /**
     * 登录校验
     *
     * @param loginDto 登录参数
     * @return 已登录用户 null为登录失败
     */
    protected CuUser loginCheck(LoginDto loginDto) {
        String account = loginDto.getAccount();
        Short mode = LoginConstants.USERNAME_LOGIN;
        //判断用什么登录
        if (Pattern.matches("^1\\d{10}$", account)) {
            mode = LoginConstants.PHONE_LOGIN;
        } else if (Pattern.matches("^\\w+([-+.]\\w+)*@\\w+([-.]\\w+)*\\.\\w+([-.]\\w+)*$", account)) {
            mode = LoginConstants.EMAIL_LOGIN;
        }
        CuUser cuUser;
        switch (mode) {
            case 0:
            default:
                cuUser = this.getOne(Wrappers.<CuUser>lambdaQuery().eq(CuUser::getUsername, account));
                break;
            case 1:
                cuUser = this.getOne(Wrappers.<CuUser>lambdaQuery().eq(CuUser::getPhone, account));
                break;
            case 2:
                cuUser = this.getOne(Wrappers.<CuUser>lambdaQuery().eq(CuUser::getEmail, account));
        }
        if (cuUser == null) {
            throw new ServiceException(HttpCodeEnum.LOGIN_INFO_ERROR);
        }

        //验证密码
        String cuUserPassSalt = cuUser.getPassword();
        String salt = cuUser.getSalt();
        //对用户输入的密码加盐比较
        String inputPassSalt = MD5Util.encodeWithSalt(loginDto.getPassword(), salt);
        return inputPassSalt.equals(cuUserPassSalt) ? cuUser : null;
    }
}
